package com.zlop.facade.impl;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.zlop.cache.RedisCacheClient;
import com.zlop.common.constant.Consts;
import com.zlop.common.exception.BaseException;
import com.zlop.common.response.CodeEnum;
import com.zlop.common.response.RestfulResponse;
import com.zlop.config.WebConfig;
import com.zlop.facade.TransFacade;
import com.zlop.model.SysAccountDO;
import com.zlop.service.SysAccountService;

/**
 * 交易Facade实现
 * 
 * @author	曾令鹏
 * @since	1.0
 * @version 2018年5月8日 曾令鹏
 * @param <T> 数据类型
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class TransFacadeImpl implements TransFacade {
	
	/** redis缓存key前缀 */
	private final static String REPEAT_DATA = "repeatData:";
	
	/** redis缓存工具类 */
	@Autowired
	private RedisCacheClient redisCacheClient;
	
	/** 系统账户表Service */
	@Autowired
	private SysAccountService sysAccountService;

	@Override
	public RestfulResponse<Void> transfer(String accountOut, String accountIn,
			String uniqId, Long transAmount) {
		// 过滤重复提交
		if(repeatDataValidator(accountOut, uniqId)){
			return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "数据已经提交");
		}
		// 参数校验
		Preconditions.checkArgument(StringUtils.isNotBlank(accountOut), "转出账号不能为空");
		Preconditions.checkArgument(StringUtils.isNotBlank(accountIn), "转入账号不能为空");
		Preconditions.checkArgument(!Objects.equal(accountOut, accountIn), "转出和转入账号不能相同");
		Preconditions.checkArgument(StringUtils.isNotBlank(uniqId), "唯一标识不能为空");
		Preconditions.checkArgument(NumberUtils.toLong(String.valueOf(transAmount), Consts.ZERO) > Consts.ZERO, "支付金额不能为空并且必须大于" + Consts.ZERO);
		try{
			// 对转出账号和输入账号排序，防止sql死锁
			int compareTo = accountOut.compareTo(accountIn);
			RestfulResponse<Void> transferOut = null;
			RestfulResponse<Void> transferIn = null;
			if(compareTo > 0){
				// 转出
				transferOut = this.transferOut(accountOut, transAmount);
				
				// 转入
				transferIn = this.transferIn(accountIn, transAmount);
			}else{
				// 转入
				transferIn = this.transferIn(accountIn, transAmount);
				
				// 转出
				transferOut = this.transferOut(accountOut, transAmount);
			}
			//校验结果
			if(transferOut != null && transferIn != null){
				if(transferOut.getCode() != CodeEnum.SUCCESS.getCode()){
					return transferOut;
				}
				if(transferIn.getCode() != CodeEnum.SUCCESS.getCode()){
					return transferIn;
				}
				if(transferOut.getCode() == CodeEnum.SUCCESS.getCode() && 
						transferIn.getCode() == CodeEnum.SUCCESS.getCode()){
					return RestfulResponse.build("转账成功");
				}
			}
		}catch(Exception e){
			Object[] args = new Object[]{accountOut, accountIn, uniqId, transAmount};
			throw new BaseException(WebConfig.getApplicationName(), CodeEnum.UPDATE_ERROR.getCode(), args, "转账异常", e);
		}
		// 回滚
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "转账失败");
	}
	
	/**
	 * 对指定账号accountOut转出指定金额transAmount
	 * 
	 * @param accountOut 账号
	 * @param transAmount 金额
	 * @return Restful 处理结果
	 * @throws Exception 出错抛出异常
	 */
	private RestfulResponse<Void> transferOut(String accountOut, Long transAmount) throws Exception{
		SysAccountDO sysAccountOut = sysAccountService.getEntity(accountOut);
		if(sysAccountOut == null){
			return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "转出账号不存在");
		}
		if(NumberUtils.toLong(String.valueOf(sysAccountOut.getAmounts())) < transAmount.longValue()){
			return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "余额不足");
		}
		sysAccountOut.setAmounts(NumberUtils.toLong(String.valueOf(sysAccountOut.getAmounts())) - transAmount.longValue());
		int outReturn = sysAccountService.update(sysAccountOut);
		if(outReturn > 0){
			return RestfulResponse.build("转出成功");
		}
		return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "转出失败");
	}
	
	/**
	 * 对指定账号accountOut转入指定金额transAmount
	 * 
	 * @param accountOut 账号
	 * @param transAmount 金额
	 * @return Restful 处理结果
	 * @throws Exception 出错抛出异常
	 */
	private RestfulResponse<Void> transferIn(String accountIn, Long transAmount) throws Exception {
		SysAccountDO sysAccountIn = sysAccountService.getEntity(accountIn);
		if(sysAccountIn == null){
			return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "转入账号不存在");
		}
		sysAccountIn.setAmounts(NumberUtils.toLong(String.valueOf(sysAccountIn.getAmounts())) + transAmount.longValue());
		int inReturn = sysAccountService.update(sysAccountIn);
		if(inReturn > 0){
			return RestfulResponse.build("转入成功");
		}
		return RestfulResponse.build(CodeEnum.BUSINESS_ERROR, "转入失败");
	}
	
	/**
	 * 校验当前账号数据是否重复提交 
	 * 
	 * @param account 账号
	 * @param uniqId 唯一标识 必选
	 * @return 重复返回true，反之false
	 */
	private boolean repeatDataValidator(String account, String uniqId) {
		// 获取本次提交的数据
		String key = REPEAT_DATA + account + ":" + uniqId;
		long incr = redisCacheClient.incr(key, 1);
		if(incr == 1){
			// 设定过期时间
			redisCacheClient.expire(key, 120);
			return false;
		}
		return true;
	}

}
